package com.agileplex.codeminer.scm.svn;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import com.agileplex.codeminer.scm.IRepository;
import com.agileplex.codeminer.scm.IRepositoryListener;
import com.agileplex.codeminer.scm.RepositoryStatus;

/**
 * Mine information for subversion repository
 * 
 * @creator Weiming Yin
 * @date 2007-6-5
 * 
 * @lastmodified
 * @author $Author: yinweiming $
 * @version $Revision: 15 $, $Date: 2007-07-16 18:12:59 +0000 (Mon, 16 Jul 2007) $
 */
public class SvnRepository implements IRepository<SvnVersion, String> {
	static Logger logger = Logger.getLogger(SvnRepository.class);
	static {
		/*
		 * For using over http:// and https://
		 */
		DAVRepositoryFactory.setup();

		/*
		 * For using over svn:// and svn+xxx://
		 */
		SVNRepositoryFactoryImpl.setup();

		/*
		 * For using over file:///
		 */
		FSRepositoryFactory.setup();
	}

	String url;

	String username;

	String passwd;

	long lastestVersion = -1;

	RepositoryStatus status = RepositoryStatus.BEFORECONNECT;

	SVNRepository repository;

	List<SvnVersion> versionList;

	SvnVersionFS fsTree;

	String uuid;
	
	List<IRepositoryListener> listenerList = new ArrayList<IRepositoryListener>();

	public SvnRepository(String url) {
		this(url, null, null);
	}

	public SvnRepository(String url, String username, String passwd) {
		this.url = url;
		this.username = username;
		this.passwd = passwd;
	}

	public synchronized void connect() {
		if (status == RepositoryStatus.CONNECTED || status == RepositoryStatus.MINING || status == RepositoryStatus.FINISH) {
			return;
		}

		try {
			repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(this.url));
			//SVNURL root = repository.getRepositoryRoot(true);
			//repository.closeSession();
			//repository = SVNRepositoryFactory.create(root);
			
			lastestVersion = repository.getLatestRevision();
			uuid = repository.getRepositoryUUID(true);
			if (uuid == null) {
				uuid = "";
			}
		} catch (SVNException e) {
			logger.error(e);
			status = RepositoryStatus.CONNECTFAILED;
		}
		ISVNAuthenticationManager authMgr = null;
		if (username != null) {
			authMgr = SVNWCUtil.createDefaultAuthenticationManager(username, passwd);
		} else {
			authMgr = SVNWCUtil.createDefaultAuthenticationManager();
		}

		repository.setAuthenticationManager(authMgr);
	}

	public RepositoryStatus getStatus() {
		return status;
	}

	public void mine() {
		this.status = RepositoryStatus.MINING;
		startToMine();
	}

	private void startToMine() {
		fsTree = new SvnVersionFS(this);
		final SvnRepository self = this;
		new Thread(new Runnable() {
			public void run() {
				for (long i = lastestVersion; i >= 0; i--) {
					SvnVersion v = SvnVersion.getSvnVersion(self, i);
					SvnDirectory root = new SvnDirectory(v, "");
					try {
						listEntries(repository, v, "", root);
						fsTree.setRootDir(root);
						logger.info(self.url + " -- " + v + " is ready");
						for (Iterator<IRepositoryListener> it = listenerList.iterator(); it.hasNext();) {
							it.next().processRepository(IRepositoryListener.ScmStatus.VERSION_READY, self, v);
						}
					} catch (SVNException e) {
						logger.error(e);
					}
				}
			}

			void listEntries(SVNRepository repository, SvnVersion version, String path, SvnDirectory parent) throws SVNException {
				Collection entries = repository.getDir(path, version.getVersion(), null, (Collection) null);
				for (Iterator it = entries.iterator(); it.hasNext();) {
					SVNDirEntry entry = (SVNDirEntry) it.next();
					System.out.println("[" + version + "] " + entry);
					if (entry.getKind() == SVNNodeKind.DIR) {
						SvnDirectory dir = new SvnDirectory(version, entry.getName());
						parent.addDir(dir);
						listEntries(repository, version, (path.equals("")) ? entry.getName() : path + "/" + entry.getName(), dir);
					} else {
						SvnFile f = new SvnFile(entry.getName(), version, parent);
						f.setAuthor(entry.getAuthor());
						f.setDate(entry.getDate());
						
						Map fileProperties = new HashMap();
						ByteArrayOutputStream baos = new ByteArrayOutputStream();
						self.repository.getFile(entry.getName(), entry.getRevision(), fileProperties, baos);
						
						String mimeType = (String) fileProperties.get(SVNProperty.MIME_TYPE);
						f.setMimeType(mimeType);
						
						if (f.isTextFile()) {
							byte[] buf = new byte[baos.size()];
							try {
								baos.write(buf);
								f.setContent(new String(buf));
							} catch (IOException e) {
								logger.error(e);
							}
						}
						parent.addFile(f);
					}
				}
			}
		}).start();
	}

	public SvnVersionFS getFSTree() {
		return fsTree;
	}

	public String getUniqId() {
		return uuid;
	}

	public SvnVersion getLastestVersion() {
		return SvnVersion.getSvnVersion(this, lastestVersion);
	}
	
	public void addRepositoryListener(IRepositoryListener listener) {
		listenerList.add(listener);
	}

	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + (int) (lastestVersion ^ (lastestVersion >>> 32));
		result = PRIME * result + ((passwd == null) ? 0 : passwd.hashCode());
		result = PRIME * result + ((url == null) ? 0 : url.hashCode());
		result = PRIME * result + ((username == null) ? 0 : username.hashCode());
		result = PRIME * result + ((uuid == null) ? 0 : uuid.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final SvnRepository other = (SvnRepository) obj;
		if (lastestVersion != other.lastestVersion)
			return false;
		if (passwd == null) {
			if (other.passwd != null)
				return false;
		} else if (!passwd.equals(other.passwd))
			return false;
		if (url == null) {
			if (other.url != null)
				return false;
		} else if (!url.equals(other.url))
			return false;
		if (username == null) {
			if (other.username != null)
				return false;
		} else if (!username.equals(other.username))
			return false;
		if (uuid == null) {
			if (other.uuid != null)
				return false;
		} else if (!uuid.equals(other.uuid))
			return false;
		return true;
	}

}
